#include <string.h>

#include "lib.h"

#include "pickguts.h"
#include "shue.h"
#include "vphand.h"
#include "cardmgr.h"
#include "vpsaved.h"

#include "pte_btr.h"	// PTE_xxx -> PayTableEvents...i.e. VPEVT_xxx

static CardMgr stdMgr;

Shue*
PickEmGuts::newShue(const ShueGameInfo &sgi)
{
	return new Shue(&stdMgr, 1, sgi);
}

CardMgr*
PickEmGuts::getCardMgr()
{
	return &stdMgr;
}

void
PickEmGuts::dealHand(Shue* shue, VPHand* hand)
{
	int32 i;

	for (i = 0; i < 8; i++)
		hand->add(shue->getCard());
}

void
PickEmGuts::makeDealtHandForClient(VPHand* src, VPHand* dest)
{
	dest->add(src->getCardAt(0));
	dest->add(src->getCardAt(1));
	dest->add(src->getCardAt(2));
	dest->add(src->getCardAt(5));

	// add one dummy card (invalid hand/suit)
	//
	dest->add((uint8)0x00);
}

void
PickEmGuts::rollbackDeal(Shue* shue, VPHand* hand)
{
	shue->pushBackNCards(hand->getCount());
	hand->removeAll();
}

bool8
PickEmGuts::isHoldTheseValid(uint8 held)
{
	return (bool8)(held == 0x04 || held == 0x08);
}

void
PickEmGuts::drawHand(uint8 held, Shue* shue, VPHand* hand)
{
	if (hand->getCount() == 8)
	{
		if (held == 0x04)		// remove second group
		{
			hand->removeAt(7);
			hand->removeAt(6);
			hand->removeAt(5);
		}
		else					// remove first group
		{
			hand->removeAt(2);
			hand->removeAt(2);
			hand->removeAt(2);
		}
	}
}

void
PickEmGuts::rollbackDraw(uint8 held, Shue* shue, VPHand* hand)
{
	shue->pushBackNCards(hand->getCount() + 3);
	hand->removeAll();
	dealHand(shue, hand);
}

int32
PickEmGuts::CalcPayout(int32* pEvent, VPHand* pHand, int32 Bet,
	CalcPayoutMode mode, uint8* subGameData, int32 length)
{
	if (mode == DEALT_CARDS)
		*pEvent = classifyHandWithGroups(pHand);
	else
		*pEvent = classifyHand(pHand);

	return *pEvent != VPEVT_LOSER ? PTBLENTRY(Bet-1, *pEvent) : 0;
}

void
PickEmGuts::restoreHand(Shue* shue, VPHand* hand, VPokerSavedGame* saved)
{
	shue->pushBackNCards(5 + 3);
	dealHand(shue, hand);

	ASSERT(
		hand->getCardAt(0) == saved->cards[0] &&
		hand->getCardAt(1) == saved->cards[1] &&
		hand->getCardAt(2) == saved->cards[2] &&
		hand->getCardAt(5) == saved->cards[3]);
}

uint8
PickEmGuts::restoreHeldCards(uint8* dealt, uint8* drawn, int32 num)
{
	// client never shows what was held
	//
	return 0;
}

int32
PickEmGuts::classifyHandWithGroups(VPHand* hand)
{
	uint8 countsGrp1[13], countsGrp2[13];
	int32 i, rank;
	int32 eventGrp1, eventGrp2;
	uint8 *probe;

	// hand is 2 fixed cards, the first group of three, and the second
	// group of three (i.e. the hand contains 8 cards).
	//
	// Since only one of the groups will be chosen, the best possible hand
	// at this point which is visible to the client is a 3-of-a-kind
	// (2 fixed and first card of first group, or 2 fixed and first card
	// of second group).
	//
	if (!hand || (hand && hand->getCount() != 8))
		return VPEVT_LOSER;

	memset(countsGrp1, 0, 13);	// 2:index 0, 3:index 1 ... A:index 12
	memset(countsGrp2, 0, 13);	// 2:index 0, 3:index 1 ... A:index 12

	for (i = 0; i < hand->getCount(); i++)
	{
		rank = stdMgr.getRank(hand->getCardAt(i));

		switch (i)
		{
			case 0:		// first 2 cards belong to both results
			case 1:
				++countsGrp1[rank - 2];
				++countsGrp2[rank - 2];
				break;

			case 2:		// 3rd card in hand is the 1st card of the first group
				++countsGrp1[rank - 2];
				break;

			case 5:		// 5th card in hand is the 1st card of the second group
				++countsGrp2[rank - 2];
				break;

			default:	// other cards aren't visible to the client
				break;
		}
	}

	// classify hand with the first group
	//
	if (memchr(countsGrp1, 3, 13))
		eventGrp1 = VPEVT_3OFAKIND;
	else
	{
		probe = (uint8*)memchr(countsGrp1, 2, 13);
		if (probe && (probe - countsGrp1 >= minPair-2))
			eventGrp1 = VPEVT_XXX_OR_BETTER;
		else
			eventGrp1 = VPEVT_LOSER;
	}

	// classify hand with the second group
	//
	if (memchr(countsGrp2, 3, 13))
		eventGrp2 = VPEVT_3OFAKIND;
	else
	{
		probe = (uint8*)memchr(countsGrp2, 2, 13);
		if (probe && (probe - countsGrp2 >= minPair-2))
			eventGrp2 = VPEVT_XXX_OR_BETTER;
		else
			eventGrp2 = VPEVT_LOSER;
	}

	return eventGrp1 > eventGrp2 ? eventGrp1 : eventGrp2;
}

int32
PickEmGuts::classifyHand(VPHand* pHand)
{
	uint8 counts[13];
	int32 i;
	uint8* probe, *first;
	bool8 flush = true;

	if (!pHand || (pHand && pHand->getCount() != 5))
		return VPEVT_LOSER;

	memset(counts, 0, 13);	// 2:index 0, 3:index 1 ... A:index 12

	for (i = 0 ; i < pHand->getCount(); i++)
	{
		++counts[stdMgr.getRank(pHand->getCardAt(i)) - 2];

		if (i > 0 && flush)
			flush = (bool8)(stdMgr.getSuit(pHand->getCardAt(i)) ==
				stdMgr.getSuit(pHand->getCardAt(i - 1)));
	}

	// pair, 2 pairs, 3-of-a-kind, 4-of-a-kind, full house
	//
	if (memchr(counts, 4, 13))
		return VPEVT_4OFAKIND;

	if (memchr(counts, 3, 13))
		if (memchr(counts, 2, 13))
			return VPEVT_FULLHOUSE;
		else
			return VPEVT_3OFAKIND;

	probe = (uint8*)memchr(counts, 2, 13);
	if (probe)
	{
		if (probe < counts+(13-1) && memchr(probe+1, 2, 13 - (probe-counts+1)))
			return VPEVT_2PAIR;
		else if (probe - counts >= minPair-2)
			return VPEVT_XXX_OR_BETTER;
	}

	// straights
	//
	first = probe = (uint8*)memchr(counts, 1, 13);
	i = 0;
	while (probe < counts + 13)
		if (*probe++ == 1)
			i++;
		else
			break;

	if (i == 5)
	{
		if (flush)
			if (first == counts + 10-2)
				return VPEVT_ROYALFLUSH;
			else
				return VPEVT_STRAIGHTFLUSH;

		return VPEVT_STRAIGHT;
	}

	if (i == 4 &&								// ace-low straights
		counts[CardMgr::ACE-2] && first == counts)
	{
		if (flush)
			return VPEVT_STRAIGHTFLUSH;

		return VPEVT_STRAIGHT;
	}

	return flush ? VPEVT_FLUSH : VPEVT_LOSER;
}
